iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 28
1
自我挑戰組

30天找回寫程式手感計劃!!!系列 第 28

Day28 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day4

  • 分享至 

  • xImage
  •  

天啊,右邊 iT邦幫忙鐵人賽 怎麼每天的話都這麼勵志XD
https://ithelp.ithome.com.tw/upload/images/20201004/201298737eTBJ4VbdM.png

「有些事現在不做 一輩子都不會做了 完成今天的鐵人文吧!」
始めましょう!!!!!!(讓我們開始吧!)

正片開始

今日課題:回合制

來了來了,隱挑戰重頭戲來了,
也是我一直很害怕面對的XD

讓我們試著思考一下,
一個回合:李逍遙選擇動作→趙靈兒選擇動作→兩位主角執行動作→怪動作
所以我們要把兩位主角所選擇的動作儲存下來。
然後這次隱挑戰為了降低難度,
只有攻擊沒有補血,
(所以我把李逍遙的氣療術跟趙靈兒的觀音咒拔掉了XD)
選擇攻擊後,要知道這次攻擊的量有多少,
然後怪也只有一隻,所以不會讓玩家選擇攻擊對象。(這也是為了降低難度XD)

總結一下,
所以要有一個物件陣列,
儲存各個角色這次選擇的動作,如果是選擇招式,還要儲存這次選擇的招式。

回合制的運作

先很簡單的讓回合制可以運作吧!
這邊先寫一個角色的動作函數,
裡面都先做鍵盤事件監聽。

function roleActionSelect(){
    console.log(`${roleStatus[currentActor].Name} 的回合!`);
    warMenuElement.setAttribute("data-option","attack"); // 每次選項初始值為攻擊

    switch(currentActor){
        case 0: // 李逍遙
            window.addEventListener("keydown", optionSelect);
            break;
        case 1: // 趙靈兒
            window.addEventListener("keydown", optionSelect);
            break;
        case 2: // 草叢怪
            window.addEventListener("keydown", optionSelect);
            break;
        default:
            console.log("default");
            break;
    }
}

再來,在原本 optionSelect 的函數中多加一個 Enter 鍵的處理,

function optionSelect(event){
    switch(event.keyCode){
        ... 前略 ...
        case 13: // Enter 鍵
            console.log(`現在的選項是:${warMenuElement.dataset.option}`);
            window.removeEventListener("keydown", optionSelect); // 當按下 Enter 就是塵埃落定,不能再選擇選項
            if ( currentActor === 2 ){ // 0→1→2 所以當2動作完畢要回到0
                currentActor = 0;
            } else{
                currentActor += 1;
            }

            if ( roleStatus[2].HealthPoint[0] >0 ){ // 如果怪的血量 > 0 則要繼續戰鬥
                roleActionSelect();
            }
            break;
    }
    ... 後略 ...
}

再來,為了要讓戰鬥可以開始,
因此前面要加上初始值。

let currentActor = 0; // 0:李逍遙 1:趙靈兒 2:怪 與roleStatus順序一致
roleActionSelect(); // 戰鬥開始

我們要先看一次順序是不是能夠如我們想像的執行,
還有是不是怪的血量沒有歸0,
戰鬥就一直能夠持續,
來看一下 console.log 怎麼顯示的吧!
https://ithelp.ithome.com.tw/upload/images/20201004/20129873q0yM0MWwBg.png
看起來有照我們所設定的執行,
不過這邊偷說,
其實我剛在寫這段的時候腦袋有點打結,
改了好幾次才改到現在這個樣子orz

再來就是要把角色所選擇的動作存下來了,
然後這邊想了一下,
選擇的動作既然是綁在角色上,
那何不把動作記在 roleStatus 裡就好呢?
That's right!!!!!

let roleStatus = [
    {   Name: "李逍遙",
        HealthPoint: [194,194], // HP(血量): 現在剩餘/總量
        MagicPoint: [129,129], // MP(法力): 現在剩餘/總量
        AttackPower: 25, // 攻擊力,決定普攻跟法攻的攻擊底量
        DefensePower: 20, // 防禦力,決定被攻擊後會損多少血
        Moves: [ // 角色會的招式
            {   MoveName: "御劍術",
                Cost: 10, // 招式所消耗法力
            },
        ],
        Action: {
            OptionSelection: "", // 選擇的選項
            SkillSelection: "", // 如果選擇的是招式,則要儲存選擇的招式
        },
    },
    ... 中略 ...
];

所以在 roleStatus 裡面多了一個 Action 的物件,
裡面放 OptionSelection 跟 SkillSelection 的屬性。
然後在 optionSelect 的函數裡,
按下 Enter 鍵的條件中,
儲存各角色 OptionSelection。

function optionSelect(event){
    switch(event.keyCode){
        ... 中略 ...
        case 13: // Enter 鍵
            // console.log(`現在的選項是:${warMenuElement.dataset.option}`);
            window.removeEventListener("keydown", optionSelect); // 當按下 Enter 就是塵埃落定,不能再選擇選項
            roleStatus[currentActor].Action.OptionSelection = warMenuElement.dataset.option;
            console.log(`${roleStatus[currentActor].Name} 選擇的動作是 ${roleStatus[currentActor].Action.OptionSelection}`);
            ... 後略 ...
    }

一樣看看呈現結果:
https://ithelp.ithome.com.tw/upload/images/20201004/20129873Vk95u2IDyB.png

加上執行扣血的機制

再來就是要加上兩位主角選擇攻擊後執行扣血的機制了,
然後剛寫了一下稍微推翻了一些之前寫過的東西,
這邊重新順一下邏輯,
角色順序是:0→1→2
當輪到2(怪),就要開始執行動作(扣血),
且不需要有鍵盤事件監聽。
所以 roleActionSelect 改成這樣:

function roleActionSelect(){
    console.log(`${roleStatus[currentActor].Name} 的回合!`);
    warMenuElement.setAttribute("data-option","attack"); // 每次選項初始值為攻擊

    switch(currentActor){
        case 0: // 李逍遙
            window.addEventListener("keydown", optionSelect);
            break;
        case 1: // 趙靈兒
            window.addEventListener("keydown", optionSelect);
            break;
        case 2: // 草叢怪
            roleActionExecute();
            break;
        default:
            console.log("default");
            break;
    }
}

執行動作的函數 roleActionExecute 分成主角跟怪,
先主角執行動作,再來怪執行動作。
(以下都只先考慮怪會掛掉的情況,主角掛掉的情況先不考慮)

  1. 主角執行動作就是要先算出這次動作的攻擊量,
    再將怪的血扣掉。
  2. 怪執行動作也是要先算出這次動作的攻擊量,
    再將主角其中一位的血扣掉。(這邊我先寫死是扣趙靈兒的血)
  3. 怪執行動作後,要再將做動的角色設回0,並再執行roleActionSelect。
function roleActionExecute(){
    // 主角執行動作
    for ( let i=0; i<2; i++ ){
        switch(roleStatus[i].Action.OptionSelection){
            case "attack": // 動作為攻擊
                let attackQty = roleStatus[i].AttackPower - roleStatus[2].DefensePower;
                roleStatus[2].HealthPoint[0] -= attackQty;
                console.log(`現在 ${roleStatus[2].Name} 的血量為 ${roleStatus[2].HealthPoint[0]}`);
                break;
            default:
                console.log("default");
                break;
        }
        // 如果怪的血量 <= 0,表示怪被消滅了,後續動作不用執行
        if ( roleStatus[2].HealthPoint[0] <=0 ){
            console.log("戰鬥勝利");
            break;
        }
    }
    // 如果怪的血量 > 0 才能執行動作
    if ( roleStatus[2].HealthPoint[0] >0 ){
        // 怪執行動作
        if ( currentActor === 2 ){
            let attackQty = roleStatus[currentActor].AttackPower - roleStatus[1].DefensePower;
            roleStatus[1].HealthPoint[0] -= attackQty;
            console.log(`現在 ${roleStatus[0].Name} 的血量為 ${roleStatus[0].HealthPoint[0]}`);
            console.log(`現在 ${roleStatus[1].Name} 的血量為 ${roleStatus[1].HealthPoint[0]}`);
        }
        // 怪執行動作後,要再將做動的角色設回0,並再執行roleActionSelect
        currentActor = 0;
        roleActionSelect();
    }
}

這邊看一下 console.log 吧!
https://ithelp.ithome.com.tw/upload/images/20201004/20129873scPAy7dxCn.png
看起來 OK 了!
那這邊把 1)扣血的顯示加到血條上面 2)怪的血條會跟著實際剩血量 試試看!

扣血的顯示

這邊我一樣習慣先在 html 寫死看看,
CSS 樣式調好了再改成 JavaScript。

html:

<div class="monster">
    <div class="bloodMinus">-20</div>
    <div class="blood"></div>
</div>

https://ithelp.ithome.com.tw/upload/images/20201004/20129873EJMA0Woh0P.png
這很顯然不是我們要的樣子,
動手調整吧!
還有這邊我想加上閃爍樣式,
就會用到 Animate.css 的東西,
Animate.css 有各種特效的 CSS,
讓你使用時不需要自己弄 CSS 弄的很辛苦,
只要直接引用就好。
網站有使用教學,
https://ithelp.ithome.com.tw/upload/images/20201004/20129873Y1Fff1XeHv.png
總之就是在 html 的 head 裡面加上 CDN (CSS),

<head>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
    <link rel="stylesheet" href="css/style.css">
</head>

再來就是在要使用的標籤加上規定的 CSS 名稱即可,
例如:

<div class="bloodMinus animate__animated animate__flash">-20</div>

再來我想要讓它閃爍完就消失,
這邊就讓我們移到 JavaScript 上控制吧!

-------------- N分鐘以後 --------------

好,我越寫越多問題了XD
就是因為我將李逍遙跟趙靈兒執行動作寫在同一個函數裡面,
雖然有用 for 迴圈決定執行順序,
但是程式執行太快,
所以最後都只會顯示趙靈兒攻擊的扣血顯示。

這邊我先求有再求好,
這樣李逍遙、趙靈兒、怪的執行動作也都必須用函數各別包起來,
再一一執行,
這樣才會有執行的先後順序。

函數:boyActionExecute, girlctionExecute, monsterActionExecute
然後我的寫法大概是:

function boyActionExecute(){
    girlctionExecute();
}
function girlctionExecute(){
    monsterActionExecute();
}

這邊我先提我延遲跟血條顯示的做法就好orz
延遲我是用 Lodash debounce
像這樣:

let bloodMinusDebounce  = _.debounce(function(){
    monsterBloodMinusElement.textContent = "";
    monsterBloodMinusElement.setAttribute("class", originalClass);
    if ( roleStatus[2].HealthPoint[0] <=0 ){
        console.log("戰鬥勝利");
    } else{
        girlctionExecute();
    }
},1500);
bloodMinusDebounce();

然後血條的顯示在 JavaScript 是先算出 (現在血量/總血量)x100 再用 物件.style 設定寬度:

// 怪的血條寬度設定
let monsterBloodWidth = (roleStatus[2].HealthPoint[0]/roleStatus[2].HealthPoint[1])*100;
monsterBloodElement.style = `width: ${monsterBloodWidth}%`;

而要讓血條有慢慢變化的感覺,則是用 CSS transition
要在血條的 CSS 加上:

.blood{
    ... 前略 ...
    transition: all 0.8s;
}

這裡有大大兩年前鐵人賽的文章分享→ Day27:小事之 Transition 與 Animation

今日進度:

  1. 戰鬥一開始畫面
    https://ithelp.ithome.com.tw/upload/images/20201004/20129873QeXY6xY46X.png
  2. 李逍遙攻擊
    https://ithelp.ithome.com.tw/upload/images/20201004/20129873tSdXVSWrNq.png
  3. 趙靈兒攻擊
    https://ithelp.ithome.com.tw/upload/images/20201004/20129873BrsCw4ltR4.png
  4. 怪的血歸0,戰鬥結束
    https://ithelp.ithome.com.tw/upload/images/20201004/201298731srmlNuomH.png

[後記]

我今天的 code 真的亂到爆炸,
等一下或明天要來順一下才行orz
難怪我這次這麼害怕碰回合制,
因為我記得我去年也是搞很久orz
最後也是亂寫硬弄出來orz

明天先整理一下 code,
然後心有餘力就開始弄招式選擇吧QQ


上一篇
Day27 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day3
下一篇
Day29 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day5
系列文
30天找回寫程式手感計劃!!!36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言